package block

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math"
	"math/big"
	."blockchain.com/part_5/utils"
)

// ---------------工作量证明------------

var (
	// 最大计数器值
	MaxNonce = math.MaxInt64
)

// 难度系数
const TargetBits = 24

// 定义工作量证明
type ProofOfWork struct {
	Block  *Block
	Target *big.Int
}

// 创建新的工作量证明
func NewProofOfWork(b *Block) *ProofOfWork {
	target := big.NewInt(1)
	// target左移uint(256-TargetBits)返回target
	target.Lsh(target, uint(256-TargetBits))

	pow := &ProofOfWork{b, target}

	return pow
}

// 数据准备
func (pow *ProofOfWork) prepareData(nonce int) []byte {
/* 需要通过仅仅一个哈希，就可以识别一个块里面的所有交易，因此，先获得每笔交易的哈希，然后将它们关联起来，
 最后获得一个连接后的组合哈希。

比特币使用了一个更加复杂的技术：它将一个块里面包含的所有交易表示为一个  Merkle tree ，
然后在工作量证明系统中使用树的根哈希（root hash）。这个方法能够让我们快速检索一个块里面是否包含了某笔交易，
即只需 root hash 而无需下载所有交易即可完成判断。
*/
	data := bytes.Join(
		[][]byte{
			pow.Block.PreBlockHash,
			pow.Block.HashTransactions(),// 改变，以前使用pow.block.Data
			IntToHex(pow.Block.Timestamp),
			IntToHex(int64(TargetBits)),
			IntToHex(int64(nonce)),
		}, []byte{})

	return data
}

// 核心方法：工作量证明运行(pow核心就是nonce值)
func (pow *ProofOfWork) Run() (int, []byte) {
	var hashInt big.Int
	var hash [32]byte
	nonce := 0

	//fmt.Printf("Mining the block containing \"%s\"\n", pow.Block.Data)
	fmt.Printf("Mining a new block")
	for nonce < MaxNonce {
		data := pow.prepareData(nonce)

		hash = sha256.Sum256(data)
		fmt.Printf("\r%x", hash)
		hashInt.SetBytes(hash[:])

		if hashInt.Cmp(pow.Target) == -1 {
			break
		} else {
			nonce++
		}
	}

	fmt.Print("\n\n")

	return nonce, hash[:]
}

// 验证块的工作量证明
func (pow *ProofOfWork) Validate() bool {
	var hashInt big.Int

	data := pow.prepareData(pow.Block.Nonce)
	hash := sha256.Sum256(data)
	hashInt.SetBytes(hash[:])

	isValid := hashInt.Cmp(pow.Target) == -1

	return isValid
}
